/**
 * Java RTP Library (jlibrtp)
 * Copyright (C) 2006 Arne Kepp
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jlibrtp;

import java.net.InetSocketAddress;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * RTCP packets for Source Descriptions
 *
 * @author Arne Kepp
 */
public class RtcpPktSDES extends RtcpPkt {
    /** Logger instance. */
    private static final Logger LOGGER =
        Logger.getLogger(RtcpPktSDES.class.getName());

    /** Whether the RTP Session object should be included */
    boolean reportSelf = true;
    /** The parent RTP Session object, holds participant database */
    RTPSession rtpSession = null;
    /** The participants to create SDES packets for */
    protected Participant[] participants = null;

    /**
     * Constructor to create a new SDES packet
     *
     * TODO:
     * Currently the added participants are not actually encoded
     * because the library lacks some support for acting as mixer or
     * relay in other areas.
     *
     * @param reportThisSession include information from RTPSession as a participant
     * @param rtpSession the session itself
     * @param additionalParticipants additional participants to include
     */
    protected RtcpPktSDES(boolean reportThisSession, RTPSession rtpSession, Participant[] additionalParticipants) {
        super.packetType = 202;
        // Fetch all the right stuff from the database
        reportSelf = reportThisSession;
        participants = additionalParticipants;
        this.rtpSession = rtpSession;
    }

    /**
     * Constructor that parses a received packet
     *
     * @param aRawPkt the byte[] containing the packet
     * @param start where in the byte[] this packet starts
     * @param socket the address from which the packet was received
     * @param partDb the participant database
     */
    protected RtcpPktSDES(byte[] aRawPkt,int start, InetSocketAddress socket, ParticipantDatabase partDb) {
        if(LOGGER.isLoggable(Level.FINEST)) {
            LOGGER.finest("  -> RtcpPktSDES(byte[], ParticipantDabase)");
        }
        rawPkt = aRawPkt;

        if(! super.parseHeaders(start) || packetType != 202 ) {
            if(LOGGER.isLoggable(Level.FINEST)) {
                LOGGER.finest(" <-> RtcpPktSDES.parseHeaders() etc. problem");
            }
            super.problem = -202;
        } else {
            //System.out.println(" DECODE SIZE: " + super.length + " itemcount " + itemCount );

            int curPos = 4 + start;
            int curLength;
            int curType;
            long ssrc;
            boolean endReached = false;
            boolean newPart;
            this.participants = new Participant[itemCount];

            // Loop over SSRC SDES chunks
            for(int i=0; i< itemCount; i++) {
                ssrc = StaticProcs.bytesToUIntLong(aRawPkt, curPos);
                Participant part = partDb.getParticipant(ssrc);
                if(part == null) {
                    if(LOGGER.isLoggable(Level.FINEST)) {
                        LOGGER.finest("RtcpPktSDES(byte[], ParticipantDabase) adding new participant, ssrc:"+ssrc+" "+socket);
                    }

                    part = new Participant(socket, socket , ssrc);
                    newPart = true;
                } else {
                    newPart = false;
                }

                curPos += 4;

                //System.out.println("PRE endReached " + endReached + " curPos: " + curPos + " length:" + this.length + (!endReached && (curPos/4) < this.length));

                while(!endReached && (curPos/4) <= this.length) {
                    //System.out.println("endReached " + endReached + " curPos: " + curPos + " length:" + this.length);
                    curType = (int) aRawPkt[curPos];

                    if(curType == 0) {
                        curPos += 4 - (curPos % 4);
                        endReached = true;
                    } else {
                        curLength  = (int) aRawPkt[curPos + 1];
                        //System.out.println("curPos:"+curPos+" curType:"+curType+" curLength:"+curLength+" read from:"+(curPos + 1));

                        if(curLength > 0) {
                            byte[] item = new byte[curLength];
                            //System.out.println("curPos:"+curPos+" arawPkt.length:"+aRawPkt.length+" curLength:"+curLength);
                            System.arraycopy(aRawPkt, curPos + 2, item, 0, curLength);

                            switch(curType) {
                            case 1:  part.cname = new String(item); break;
                            case 2:  part.name = new String(item); break;
                            case 3:  part.email = new String(item); break;
                            case 4:  part.phone = new String(item); break;
                            case 5:  part.loc = new String(item); break;
                            case 6:  part.tool = new String(item); break;
                            case 7:  part.note = new String(item); break;
                            case 8:  part.priv = new String(item); break;
                            }
                            //System.out.println("TYPE " + curType + " value:" + new String(item) );

                        } else {
                            switch(curType) {
                            case 1:  part.cname = null; break;
                            case 2:  part.name = null; break;
                            case 3:  part.email = null; break;
                            case 4:  part.phone = null; break;
                            case 5:  part.loc = null; break;
                            case 6:  part.tool = null; break;
                            case 7:  part.note = null; break;
                            case 8:  part.priv = null; break;
                            }

                        }
                        curPos = curPos + curLength + 2;
                    }
                }

                // Save the participant
                this.participants[i] = part;
                if(newPart)
                    partDb.addParticipant(2,part);

                //System.out.println("HEPPPPPP " + participants[i].cname );
            }
        }
        if(LOGGER.isLoggable(Level.FINEST)) {
            LOGGER.finest("  <- RtcpPktSDES()");
        }
    }

    /**
     * Encode the packet into a byte[], saved in .rawPkt
     *
     * CompRtcpPkt will call this automatically
     */
    protected void encode() {
        byte[] temp = new byte[1450];
        byte[] someBytes = StaticProcs.uIntLongToByteWord(this.rtpSession.ssrc);
        System.arraycopy(someBytes, 0, temp, 4, 4);
        int pos = 8;

        String tmpString = null;
        for(int i=1; i<9;i++) {
            switch(i) {
            case 1:  tmpString = this.rtpSession.cname; break;
            case 2:  tmpString = this.rtpSession.name; break;
            case 3:  tmpString = this.rtpSession.email; break;
            case 4:  tmpString = this.rtpSession.phone; break;
            case 5:  tmpString = this.rtpSession.loc; break;
            case 6:  tmpString = this.rtpSession.tool; break;
            case 7:  tmpString = this.rtpSession.note; break;
            case 8:  tmpString = this.rtpSession.priv; break;
            }

            if(tmpString != null) {
                someBytes = tmpString.getBytes();
                temp[pos] = (byte) i;
                temp[pos+1] = (byte) someBytes.length;
                System.arraycopy(someBytes, 0, temp, pos + 2, someBytes.length);
                //System.out.println("i: "+i+" pos:"+pos+" someBytes.length:"+someBytes.length);
                pos = pos + someBytes.length + 2;
                //if(i == 1 ) {
                //	System.out.println("trueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + tmpString);
                //}
            }
        }
        int leftover = pos % 4;
        if(leftover == 1) {
            temp[pos] = (byte) 0;
            temp[pos + 1] = (byte) 1;
            pos += 3;
        } else if(leftover == 2) {
            temp[pos] = (byte) 0;
            temp[pos + 1] = (byte) 0;
            pos += 2;
        } else if(leftover == 3) {
            temp[pos] = (byte) 0;
            temp[pos + 1] = (byte) 3;
            pos += 5;
        }

        // TODO Here we ought to loop over participants, if we're doing SDES for other participants.

        super.rawPkt = new byte[pos];
        itemCount = 1;
        //This looks wrong, but appears to be fine..
        System.arraycopy(temp, 0, super.rawPkt, 0, pos);
        writeHeaders();
    }

    /**
     * Debug purposes only
     */
    public void debugPrint() {
        LOGGER.finest("RtcpPktSDES.debugPrint() ");
        if(participants != null) {
            for(int i= 0; i<participants.length; i++) {
                Participant part = participants[i];
                LOGGER.finest("     part.ssrc: " + part.ssrc + "  part.cname: " + part.cname + " part.loc: " + part.loc);
            }
        } else {
            LOGGER.finest("     nothing to report (only valid for received packets)");
        }
    }
}
